# -*- coding:utf-8 -*-

from fastapi import Depends
from typing import Type
from enum import Enum
from sqlalchemy.orm import Session
from core.database import Base, get_db, get_model_class_by_table_name
from core.dependencies import page_filter, PageFilter

from app.models.data import TbAssociation
from app.models.request import BaseModelLocal


from app.logger import getLogger

logger = getLogger()

class BaseRepository:
    def __init__(self, db: Session=Depends(get_db)):
        self.db = db
    
    async def get_filter(self, *, filter: dict={}, like: dict={}, in_: dict={}, order_by: dict = {}):
        return self.table.filter(self.db, filter, like, in_).order_by(*self.table.order_by(order_by))

    async def get_by_page(self, page_no:int=1, page_size:int=10, page_filter: PageFilter = None):
        filter = self.table.filter(self.db, page_filter.filter, page_filter.like, page_filter.in_).order_by(*self.table.order_by(page_filter.order_by))
        total_rows = filter.count()
        skip = (page_no - 1) * page_size
        datas = filter.offset(skip).limit(page_size).all()
        datas = [self.model.from_orm(data) for data in datas]
        total_page = total_rows // page_size if total_rows % page_size else total_rows // page_size + 1
        return datas, total_page, total_rows

    async def get_by_id(self, id: int):
        return self.db.query(self.table).filter(self.table.id==id, self.table.del_flag==0).one_or_none()
    
    async def get_by_ids(self, ids: list[int]):
        return self.db.query(self.table).filter(self.table.id.in_(ids), self.table.del_flag==0).all()
    
    async def get_model_by_id(self, id: int) -> BaseModelLocal:
        data = self.db.query(self.table).filter(self.table.id==id, self.table.del_flag==0).one_or_none()
        if data is None: return None
        return self.model.from_orm(await self.get_by_id(id))
    
    async def get_ref_by_ids(self, ids: list[int], ref_tablename: str, association_type: str, ref_type)->dict:
        ref_table = get_model_class_by_table_name(ref_tablename)
        associations = self.db.query(TbAssociation, ref_table).join(ref_table, TbAssociation.target_id == ref_table.id).filter(TbAssociation.source_id.in_(ids), TbAssociation.association_type == association_type, TbAssociation.del_flag==0).all()
        return {id: [ref_type.from_orm(association[1]) for association in associations if association.TbAssociation.source_id == int(id)] for id in ids}

    async def delete_ref(self, id: int, target_ids: list[int], ref_tablename: str, association_type: str, delete_user: str | int | None = None, commit=False, flush: bool = True):
        ref_table = get_model_class_by_table_name(ref_tablename)
        associations = self.db.query(TbAssociation).filter(TbAssociation.source_id == id, TbAssociation.target_id.in_(target_ids), TbAssociation.association_type == association_type, TbAssociation.del_flag==0).all()
        delete_count = 0
        for association in associations:
            association.del_flag = 1
            association.update_user = delete_user
            delete_count += 1
            
        logger.info(f"delete_count: {delete_count}")
        if commit:
            self.db.commit()
        elif flush:
            self.db.flush()

    async def add_ref(self, id: int, target_ids: list[int], ref_tablename: str, association_type: str, create_user: str | int | None = None, commit=False, flush: bool = True):
        ref_table = get_model_class_by_table_name(ref_tablename)
        associations = []
        for target_id in target_ids:
            association = TbAssociation(source_id=id, target_id=target_id, association_type=association_type, create_user=create_user, del_flag=0)
            associations.append(association)
        self.db.add_all(associations)
        logger.info(f"add_count: {len(associations)}")
        if commit:
            self.db.commit()
        elif flush:
            self.db.flush()

    async def save(self, data: BaseModelLocal, another: dict={}, commit=False, create_user: str | int | None = None, flush: bool = True) -> BaseModelLocal:
        db_data = self.table(**data.dict())
        for key, val in another.items():
            if key not in self.table.__table__.columns: continue
            setattr(db_data, key, val)
        db_data.create_user = create_user
        return self.model.from_orm(db_data.add(self.db, commit, flush))

    async def update(self, id: int, data: BaseModelLocal, commit=False, update_user: str | int | None = None, flush: bool = True) -> BaseModelLocal:
        db_data = await self.get_by_id(id)
        db_data.update_user = update_user
        return self.model.from_orm(db_data.update(self.db, {key: val.value if isinstance(val, Enum) else val for key, val in data.dict().items()}, commit, flush))

    async def delete(self, id: int, commit=False, delete_user: str | int | None = None, flush: bool = True) -> BaseModelLocal:
        return self.model.from_orm(await self.update(id, self.model.from_orm({"id": id, "update_user": delete_user, "del_flag": 1}), commit, flush))
        # return db_data.delete(self.db, commit, flush)

    async def commit(self):
        return self.db.commit()

    async def rollback(self):
        return self.db.rollback()

    async def flush(self):
        return self.db.flush()